#!/bin/bash
# a tool to detect whitespace issues in source files or executable files
# with the -F option, it should repair the files
# This should be a superset of what jcheck is looking for

showHelp() {
    cat <<-EOF
Usage: $0 [-avSF] [-r <spec>]
  -a           : use repo manifest for check
  -v           : verbose
  -V           : very verbose (debug)
  -S           : read file list from stdin
  -E           : extra extensions check
  -F           : fix files
  -r <spec>    : mercurial revision spec
  -x           : check for the executable bit (not on windows)
EOF
    exit 1
}

# directory this script is in
MYDIR="$( dirname $0 )"

verbose=''
veryverbose=''
allrepo=''
fixfiles=''
readstdin=''
revisionspec=''
executablecheck=''
extraextlist=''

tofixargs="-F"

while getopts ":avEFSVxr:" opt;
do
    case $opt in
    a)
        allrepo='true'
        tofixargs="${tofixargs} -a"
        ;;
    S)
        readstdin='true'
        ;;
    v)
        verbose='true'
        ;;
    V)
        verbose='true'
        veryverbose='true'
        ;;
    E)
        extraextlist='true'
        ;;
    F)
        fixfiles='true'
        ;;
    r)
        revisionspec="$OPTARG"
        ;;
    x)
        executablecheck='true'
        tofixargs="${tofixargs} -x"
        ;;
    \?)
        echo "Invalid argument $OPTARG"
        showHelp
        ;;
    :)
        echo "Option -$OPTARG requires an argument." >&2
        showHelp
        ;;
    esac
done

# this is a preferred extension list, but jcheck needs an extension to honor it
#*.java|*.cc|*.c|*.h|*.cpp|*.hpp|*.jsl|*.fxml|*.css|*.m|*.mm|.*frag|.vert|*.hlsl|*.gradle|*.groovy
# until then, just use the jcheck built in extension list

extensions_to_check='*.java|*.c|*.h|*.cpp|*.hpp'
match_extension() {
    if [ ! -z "$extraextlist" ]
    then
        case "$1" in
        *.java|*.cc|*.c|*.h|*.cpp|*.hpp|*.jsl|*.fxml|*.css|*.m|*.mm|.*frag|.vert|*.hlsl|*.gradle|*.groovy)
            return 0
            ;;
        esac
    else
        case "$1" in
        *.java|*.c|*.h|*.cpp|*.hpp)
            return 0
            ;;
        esac
    fi
    return 1
}

fix_file() {
    endstatus=1
    if [ "$executablecheck" -a -x "${file}" ]
    then
        chmod a-x "${file}"
        echo "${file}: execute corrected"
        endstatus=0
    fi
    match_extension "${file}"
    if [ $? -eq 0 ]
    then
        grep -U -q -e $'\t'  -e '[[:blank:]]$' -e $'\r' ${file}
        if [ $? -eq 0 ]
        then
            expand -4 < "${file}" | tr -d "\r" | sed -e 's/[[:blank:]]*$//' > "${file}.fws"
            if [ -f "${file}.fws" ]
            then
                rm -f "${file}"
                mv "${file}.fws" "${file}"
                echo "${file}: fixed"
            else
                echo "Failed to fix ${file}"
                exit -1
            fi
            endstatus=0
        else
            [ "$verbose" ] &&
                echo "${file}: no change"
        fi
    fi
    return $endstatus
}

check_file() {
    file="$1"
    message=':'
    if [ "$executablecheck" -a -x "${file}" ]
    then
       message="executable:"
    fi
    match_extension "${file}"
    if [ $? -eq 0 ]
    then
        grep -Uqs -e $'\t'  -e '[[:blank:]]$' -e $'\r' "${file}"
        if [ $? -eq 0 ]
        then
            grep -Uqs -e $'\t' "${file}"
            [ $? -eq 0 ] &&
                message="${message}tabs:"
            grep -Uqs -e '[[:blank:]]$' "${file}"
            [ $? -eq 0 ] &&
                message="${message}trailingWhitespace:"
            grep -Uqs -e $'\r' "${file}"
            [ $? -eq 0 ] &&
                message="${message}DOS:"
        fi
    fi
    if [ "$message" != ":" ]
    then
        echo "${file} ${message}"
        return 0
    else
        [ "$verbose" ] && echo "${file} :OK"
        return 1
    fi
}

process_files() {
    let fails=0
    while read file
    do
       if [ "$fixfiles" ]
       then
           fix_file "${file}"
           [ $? -eq 0 ] && let fails=fails+1
       else
           check_file "${file}"
           [ $? -eq 0 ] && let fails=fails+1
       fi
    done

    # show a message if we are not correcting the files
    if [ "$fails" -ne 0 -a -z "$fixfiles" ]
    then
        echo
        echo "Found $fails whitespace or executable issues"
        echo
        echo "To correct, use"
        echo "   bash ${MYDIR}/checkWhiteSpace ${tofixargs}"
        echo
        exit 1
    elif [ "$fails" -ne 0 -a "$fixfiles" ]
    then
        echo
        echo "Corrected $fails whitespace or executable issues"
        echo
        exit 1
    else
        exit 0
    fi
}

if [ "$allrepo" ]
then
   hg man | process_files
   exit $?
fi

if [ "$readstdin" ]
then
   process_files
   exit $?
fi

# check for "loose" files and check them
loosefiles=`hg status -m -a -n `
if [ ! -z "$loosefiles" -a  -z "$revisionspec" ]
then
   [ "$verbose" ] && echo "Checking uncommitted files"
   hg status -m -a -n | process_files
   exit $?
fi

if [ -z "$revisionspec" ]
then
    # check for either an applied set of MQ patches
    # or the outgoing changeset

    hg qtop -s > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
       [ "$verbose" ] && echo "Checking MQ files"
        revisionspec="qbase:qtip"
    fi

    # no loose files, no MQ, lets use outgoing changes
    if [ -z "$revisionspec" ]
    then
        revisionspec="outgoing()"
    fi
fi

[ "$verbose" ] && echo "Using a revision range of $revisionspec"
hg log -r "$revisionspec" --style ${MYDIR}/files.style | process_files
exit $?

